package com.sky.aspect;

import com.sky.annotation.AutoFill;
import com.sky.constant.AutoFillConstant;
import com.sky.context.BaseContext;
import com.sky.enumeration.OperationType;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.time.LocalDateTime;

@Aspect     //@Aspect: 标识这是一个切面类，用于处理横切关注点
@Component  //将该类注册为Spring容器管理的组件
@Slf4j     //Lombok注解，提供日志记录功能
public class AutoFillAspect {
    /**
     * 切入点
     */
    @Pointcut("execution(* com.sky.mapper.*.*(..)) && @annotation(com.sky.annotation.AutoFill)")
    public void autoFillPointCut(){}
    //@Pointcut: AspectJ 注解，用于定义切入点表达式
    //&&前：匹配 com.sky.mapper 包下的所有类的所有方法
    //&&后：匹配有 AutoFill 注解的方法
    //这个切入点的作用是：
    //拦截所有在 com.sky.mapper 包中，且被 @AutoFill 注解标记的方法


    /**
     * 前置通知，在通知中进行公共字段赋值
     */
    @Before("autoFillPointCut()")
    //该方法将在匹配的方法执行前被调用
    public void autoFill(JoinPoint joinPoint){  //定义通知方法，参数 JoinPoint 对象提供了对目标方法的访问
        log.info("开始进行公共字段自动填充...");  //在方法执行时输出日志信息

        //使用 @Before 注解标记这是一个前置通知
        //指定在 autoFillPointCut() 切入点匹配的方法执行前触发


        //获取到当前被拦截的方法上的数据库操作类型
        MethodSignature signature = (MethodSignature) joinPoint.getSignature(); //方法签名对象
        AutoFill autoFill = signature.getMethod().getAnnotation(AutoFill.class);//获取方法上的注解对象
        OperationType operationType = autoFill.value();  //获得数据库操作类型

        //获取到当前被拦截的方法的参数，即实体对象
        //获取被拦截方法的参数
        //取第一个参数作为要操作的实体对象（如员工、菜品等
        Object[] args = joinPoint.getArgs();
        if (args == null || args.length == 0){    //防止空指针异常和参数不存在的情况
            return;
        }

        Object entity = args[0];  //不用Employee,用Object去接收，因为后期传进来的不一定是Employee

        //准备赋值的数据
        LocalDateTime now = LocalDateTime.now();
        Long currentId = BaseContext.getCurrentId();

        //根据当前不同的操作类型，为对应的属性通过反射赋值
        if (operationType == OperationType.INSERT){
            //为4个公共字段赋值
            try {
                Method setCreateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_TIME, LocalDateTime.class);
                Method setUpdateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);
                Method setCreateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_USER, Long.class);
                Method setUpdateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);

                //通过反射为对应的属性赋值
                setCreateTime.invoke(entity, now);
                setUpdateTime.invoke(entity, now);
                setCreateUser.invoke(entity, currentId);
                setUpdateUser.invoke(entity, currentId);
            }catch (Exception e){
                e.printStackTrace();
            }
        }else if (operationType == OperationType.UPDATE){
            //为2个公共字段赋值
            try {
                Method setUpdateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);
                Method setUpdateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);

                //通过反射为对应的属性赋值
                setUpdateTime.invoke(entity, now);
                setUpdateUser.invoke(entity, currentId);
            }catch (Exception e){
                e.printStackTrace();
            }}
    }
}
